package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
)

// 定义天干和地支
var tianGan = []string{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"}
var diZhi = []string{"子鼠", "丑牛", "寅虎", "卯兔", "辰龙", "巳蛇", "午马", "未羊", "申猴", "酉鸡", "戌狗", "亥猪"}

// 计算天干地支年份
// 以公元元年为坐标的算法,这里要排除公元0年，可以令公元0年为公元前1年，也就是year=-1
// 公元前的算法
// 年干=8-N(N＜8)或8-N+10(N≧8)，N=年号除以10的余数=年号个位数。
// 年支=10-N(N＜10)或10-N+12(N≧10)，N=年号除以12的余数。
// 公元后的算法
// 年干=N-3(N＞3)或N-3+10(N≤3)，N=年号除以10的余数=年号个位数。
// 年支=N-3(N＞3)或N-3+12(N≤3)，N=年号除以12的余数。

// 计算天干地支年份
// 以黄帝纪年元年天干地支的初始年份也就是公元前2697甲子年为坐标起点的算法
func getHeavenlyStemsAndEarthlyBranchesByYear(year int) (string, string) {
	tianGanIndex, diZhiIndex := 0, 0
	// **的西元纪年，公元前1年为0年，在数学上，-1和0同时占据一个点上，
	// 因此在公元纪年转化黄帝纪年时，从公元0年切开
	// 公元前
	if year <= 0 {
		// **的西元纪年，公元前1年为0年，为同一年，
		if year == 0 {
			year = -1
		}
		tianGanIndex = (year + 2697) % 10 // 天干的索引
		diZhiIndex = (year + 2697) % 12   // 地支的索引
	} else {
		// 公元后
		// 但是天干地支的纪年是记录完整的不存在这样的问题。因此在计算天干地支时，
		// 公元的年数是多计数一年的需要去除。
		tianGanIndex = (year + 2697 - 1) % 10 // 天干的索引
		diZhiIndex = (year + 2697 - 1) % 12   // 地支的索引
	}
	return tianGan[tianGanIndex], diZhi[diZhiIndex]
}

// 计算黄帝纪年
func getYellowEmperorYear(year int) int {
	// 排除公元前1年和0年的造成的虚无的公元0年。
	if year < 0 {
		year += 1
	}
	return year + 2697 // 黄帝纪年从公元前2697甲子年开始算起
}

// 计算公元纪年
func getChronologyYear(yellowEmperorYear int) int {
	// 黄帝纪年1年是公元前2697年。当difference等于0时，代表在公元0年也就是公元前1年，小于零代表在公元前，
	difference := yellowEmperorYear - 2697
	// 公元前，将2697年多算的公元0年去掉，得到真正的公元前N年
	if difference < 0 {
		return difference - 1
	}
	// 将公元0年和公元前1年合并。
	if difference == 0 {
		return -1
	}
	// 公元后，因为公元后是从元年也就是公元1年开始算的，公元后的数据和后面的黄帝纪年就能一一对上了。
	if difference > 0 {
		return difference
	}
	return 0
}

// 公元纪年转黄帝纪年
func cToY(value string) {
	year, err := strconv.Atoi(value)

	if err != nil {
		fmt.Println("Invalid number:", value)
		return
	}
	if year >= -2697 {
		// 获取黄帝纪年
		yEY := getYellowEmperorYear(year)
		// 获取天干地支年份
		tianGan, diZhi := getHeavenlyStemsAndEarthlyBranchesByYear(year)
		// 输出结果
		if year <= 0 {
			if year == 0 {
				year = -1
			}
			fmt.Printf("公元前%d年对应: 黄帝纪元%d%s%s年\n", year*-1, yEY, tianGan, diZhi)
			return
		}
		fmt.Printf("公元%d年对应: 黄帝纪元%d%s%s年\n", year, yEY, tianGan, diZhi)
	} else {
		fmt.Println("Invalid number: 公元转化黄帝纪年，要大于等于公元前2697年")
	}

}

// 黄帝纪年转公元纪年
func yToC(value string) {
	yEY, err := strconv.Atoi(value)
	if err != nil {
		fmt.Println("Invalid number:", value)
		return
	}
	if yEY > 0 {
		year := getChronologyYear(yEY)
		// 获取天干地支年份,将黄帝纪年的天干地支补全。
		tianGan, diZhi := getHeavenlyStemsAndEarthlyBranchesByYear(year)
		if year <= 0 {
			if year == 0 {
				year = -1
			}

			fmt.Printf("黄帝纪元%d%s%s年对应: 公元前%d年\n", yEY, tianGan, diZhi, year*-1)
			return
		}
		fmt.Printf("黄帝纪元%d%s%s年对应: 公元%d年\n", yEY, tianGan, diZhi, year)
	} else {
		fmt.Println("Invalid number: 黄帝纪年从黄帝元年也就是黄帝纪年1年开始。")
	}

}

func yToT(value string) {
	yEY, err := strconv.Atoi(value)
	if err != nil {
		fmt.Println("Invalid number:", value)
		return
	}
	if yEY > 0 {
		year := getChronologyYear(yEY)
		// 获取天干地支年份,将黄帝纪年的天干地支补全。
		tianGan, diZhi := getHeavenlyStemsAndEarthlyBranchesByYear(year)
		if year <= 0 {
			if year == 0 {
				year = -1
			}
			fmt.Printf("黄帝纪元%d年是%s%s年\n", yEY, tianGan, diZhi)
			return
		}
		fmt.Printf("黄帝纪元%d年是%s%s年\n", yEY, tianGan, diZhi)
	} else {
		fmt.Println("Invalid number: 黄帝纪年从黄帝元年也就是黄帝纪年1年开始。")
	}

}

// 通过参数计算转换结果
// typeFlag 参数对应的类型，c公元纪年转黄帝纪元，y黄帝纪元转公元纪年
// value 具体的纪年年数。
func getResultByParams(typeFlag, value string) {
	switch typeFlag {
	case "-c":
		cToY(value)
	case "-y":
		yToC(value)
	case "-t":
		yToT(value)
	default:
	}
}

func main() {
	var year, yellowEmperorYear, yellowEmperorYearJiNian string
	flag.StringVar(&year, "c", "-2697", "公元转化成黄帝纪年,C是公元纪年作为参数，输出黄帝纪年和天干地支，时间要在公元前2697年及以后。")
	flag.StringVar(&yellowEmperorYear, "y", "1", "黄帝纪年转化成公元纪年,y是黄帝纪年作为参数，输出公元纪年。黄帝纪年要从黄帝纪元1甲子年开始。")
	flag.StringVar(&yellowEmperorYearJiNian, "t", "1", "黄帝纪年转化成公元纪年,y是黄帝纪年作为参数，输出公元纪年。黄帝纪年要从黄帝纪元1甲子年开始。")
	flag.Parse()

	// 如果没有参数，执行默认值。
	if len(os.Args) == 1 {
		cToY(year)
		yToC(yellowEmperorYear)
		yToT(yellowEmperorYearJiNian)
		return
	}
	// 如果有参数，遍历参数计算转换结果
	for i := 1; i < len(os.Args); i++ {
		if i+1 < len(os.Args) {
			getResultByParams(os.Args[i], os.Args[i+1])
		}
	}

}
